using System;
using System.Collections;
using System.Collections.Generic;
using Framework.Library.Core.Utils.Miscellaneous;
using Framework.Library.Core.Utils.Reflection;

namespace Framework.Library.Core.Utils.Collections
{
    public class EnumeratorWrapper<T> : IEnumerator<T>
    {
        private readonly bool _convertNonCompatibleType;
        private readonly bool _defaultOnBadType;
        private readonly IEnumerator _enumerator;

        public EnumeratorWrapper(IEnumerator e) : this(e, false, false)
        {
        }

        public EnumeratorWrapper(IEnumerator e, bool defaultOnBadType, bool convertNonCompatibleType)
        {
            ValidationUtils.ArgumentNotNull(e, "e");

            _defaultOnBadType = defaultOnBadType;
            _convertNonCompatibleType = convertNonCompatibleType;
            _enumerator = e;
        }

        public bool DefaultOnBadType
        {
            get { return _defaultOnBadType; }
        }

        public bool ConvertNonCompatibleType
        {
            get { return _convertNonCompatibleType; }
        }

        #region IEnumerator<T> Members

        public T Current
        {
            get
            {
                object current = _enumerator.Current;

                if (IsCompatibleObject(current))
                {
                    return (T) _enumerator.Current;
                }
                else
                {
                    if (_convertNonCompatibleType)
                    {
                        T convertedValue;
                        if (ConvertUtils.TryConvert(current, out convertedValue))
                            return convertedValue;
                    }

                    if (_defaultOnBadType)
                        return default(T);
                    else
                        throw new Exception(string.Format("Error converting '{0}' to {1}.",
                                                          MiscellaneousUtils.ToString(current), typeof (T)));
                }
            }
        }

        public void Dispose()
        {
            var disposable = _enumerator as IDisposable;

            if (disposable != null)
                disposable.Dispose();
        }

        object IEnumerator.Current
        {
            get { return _enumerator.Current; }
        }

        public bool MoveNext()
        {
            return _enumerator.MoveNext();
        }

        public void Reset()
        {
            _enumerator.Reset();
        }

        #endregion

        private bool IsCompatibleObject(object value)
        {
            return (value is T || (value == null && ReflectionUtils.IsNullable(typeof (T))));
        }
    }
}